package flib;

import java.awt.geom.GeneralPath;
import java.util.Vector;


import processing.core.PGraphics;
import simpleGeom.*;

public class FlibFace implements HasBounds2D{
	RoomType type=new RoomType();
	
	FlibHENode center;
	FlibHEdge hEdge;
	public int id;
	public int groupId;
	public int buildingId=-1;
	public int levelId;
	public boolean steps;
	
	Vector<FlibHEdge>innerRings=new Vector<FlibHEdge>();
	FlibFace outerFace;
	public String getLabel(){
		if (type!=null)return type.label;
		return "area";
	}
	public FlibHEdge getHEdge() {
		// TODO Auto-generated method stub
		return hEdge;
	}
	public Vector<FlibHEdge> getHEdges() {
		// TODO Auto-generated method stub
		Vector<FlibHEdge>hEdges=new Vector<FlibHEdge>();
		FlibHEdge nHEdge = this.hEdge;
		//if (nHEdge==null)return;
		do {
			hEdges.add(nHEdge);
			nHEdge=nHEdge.getNext();
			
		}
		 while (nHEdge!=this.hEdge);
		return hEdges;
	}

	public void setHEdge(FlibHEdge cHEdge) {
		// TODO Auto-generated method stub
		hEdge=cHEdge;
		
		
	}
	
	public Vec3D getGravityCenter(){
		return Face3D.getGravityCenter(this.getFaceNodes());
	}

	public Vec3D getInnerPoint() {
		// test a grid until it fits.
		
			GeneralPath path = this.getGeneralPath();
			Rect2D bds = this.getBounds2D();
			int nIntervals = 10;
			while (nIntervals < 1000) {
				float sX = bds.getDimX() / nIntervals;
				float sY = bds.getDimY() / nIntervals;

				for (int x = 0; x < nIntervals; x++) {
					for (int y = 0; y < nIntervals; y++) {
						float cX = bds.x1 + sX * (x + 1);
						float cY = bds.y1 + sY * (y + 1);
						if (path.contains(cX, cY)) {
							return new Vec3D(cX, cY, 0);
						}
					}
				}
				nIntervals *= 10;
			}
		

		return getGravityCenter();
	}

	public void display(PGraphics g){
		g.beginShape();
		int i=0;
		FlibHEdge nHEdge = this.hEdge;
		//if (nHEdge==null)return;
		do {
			g.vertex(nHEdge.x1(),nHEdge.y1());
			nHEdge = nHEdge.getNext();
		}
		while (nHEdge!=this.hEdge);
		
		g.endShape(g.CLOSE);
	}
	public void display3D(PGraphics g){
		g.beginShape();
		int i=0;
		FlibHEdge nHEdge = this.hEdge;
		//if (nHEdge==null)return;
		do {
			g.vertex(nHEdge.x1(),nHEdge.y1(),nHEdge.z1());
			nHEdge = nHEdge.getNext();
		}
		while (nHEdge!=this.hEdge);
		
		g.endShape(g.CLOSE);
	}

	public GeneralPath getGeneralPath() {
		GeneralPath path = new GeneralPath();
		
		path.moveTo((float) hEdge.getOrg().getX(), (float) hEdge.getOrg()
				.getY());
		FlibHEdge nHEdge = hEdge.getNext();
		while (!nHEdge.equals(hEdge)) {
			//System.out.println("lenght: "+nHEdge.getLength());
			path.lineTo((float) nHEdge.getOrg().getX(), (float) nHEdge
					.getOrg().getY());
			nHEdge = nHEdge.getNext();
			
		}
		path.closePath();

		return path;
	}
	public Vector<FlibFace> getNbs() {
		Vector<FlibFace>  nbs = new Vector<FlibFace> ();
		FlibHEdge nHEdge = hEdge;
		do {
			//System.out.println(nodes.size());
			FlibFace nb=(FlibFace) nHEdge.getSym().getFace();
			if (nb!=null){
				nbs.add(nb);
			}
			nHEdge = nHEdge.getNext();
		} while (nHEdge!=hEdge);
		return nbs;
	}
	public FlibHEdge getHEdge(FlibFace cNb){
		FlibHEdge nHEdge = hEdge;
		do {
			//System.out.println(nodes.size());
			FlibFace nb=(FlibFace) nHEdge.getSym().getFace();
			if (cNb==nb){
				return nHEdge;
			}
			nHEdge = nHEdge.getNext();
		} while (nHEdge!=hEdge);
		return null;
	}
	public Vector<FlibHENode> getFaceNodes() {
		Vector<FlibHENode>  nodes = new Vector<FlibHENode> ();
		FlibHEdge nHEdge = hEdge;
		do {
			//System.out.println(nodes.size());
			nodes.add(nHEdge.getOrg());
			nHEdge = nHEdge.getNext();
		} while (nHEdge!=hEdge);
		return nodes;
	}
	
	public Rect2D calculateBounds(){
		Bounds2D b=new Bounds2D();
		FlibHEdge nHEdge = hEdge;
		do {
			FlibHENode n=nHEdge.getOrg();
			b.addPos2D(n);
			nHEdge = nHEdge.getNext();
		} while (nHEdge!=hEdge);
		return b;
	}
	
	public Face3D constructFace3D(){
		Vector<HasPos3D>  nodes = new Vector<HasPos3D> ();
		FlibHEdge nHEdge = hEdge;
		do {
			//System.out.println(nodes.size());
			nodes.add(nHEdge.getOrg());
			nHEdge = nHEdge.getNext();
		} while (nHEdge!=hEdge);
		Face3D face=new Face3D(nodes);
		return face;
	}

	public Rect2D getBounds2D() {
		// TODO Auto-generated method stub
		return calculateBounds();
	}
	public boolean containsBounds(float x, float y) {
		Rect2D r = getBounds2D();
		return r.contains(x, y);
	}
	public boolean contains(float x, float y) {
		return this.getGeneralPath().contains(x, y);
		
	}
	
	
	
	public FlibHEdge split(FlibHENode n1,FlibHENode n2){
		
		FlibHEdge hE = new FlibHEdge(n1, n2);		
		hE.attach();
		
		FlibFace newFace1 = new FlibFace();
		FlibFace newFace2 = new FlibFace();
		assignHedge(hE,newFace1);
		assignHedge(hE.getSym(),newFace2);
		return hE;
	}
	public FlibHEdge split(float x1, float y1, float x2,float y2) {
		FlibHEdge cHE = this.getHEdge();
		
		FlibHENode n1 = null;
		FlibHENode n2 = null;
		FlibHEdge he1 = null;
		FlibHEdge he2 = null;
		Vec2D iP1 = null;
		Vec2D iP2 = null;
		
		do {
			if (iP1 == null) {
				iP1 = cHE.lineIntersection(x1, y1, x2, y2);				
				if (iP1 != null) {
					he1 = cHE;
				}
			}
			else if (iP1!=null&&iP2 == null) {
				iP2 = cHE.lineIntersection(x1, y1, x2, y2);
				if (iP2 != null) {
					he2 = cHE;
				}
			}
			cHE = cHE.getNext();
		} while (cHE != this.getHEdge());
		
		
		if (iP1==null)System.out.println("iP1: null");
		if (iP2==null)System.out.println("iP2: null");
		if (iP1 != null) {
			n1 = new FlibHENode(iP1);
		}
		if (iP2 != null) {
			n2 = new FlibHENode(iP2);
		}

		if (n1 != null && n2 != null) {
			FlibFace newFace1 = new FlibFace();
			FlibFace newFace2 = new FlibFace();
			
			he1.split(n1);
			he2.split(n2);
			
			return split(n1,n2);
		}
		return null;
	}
	public void attach(FlibHEdge hEdge){
		this.hEdge=hEdge;
		this.hEdge.setFaceToAll(this);
	}
	public void attach(){
		this.hEdge.setFaceToAll(this);
	}
	public void detach(){
		this.hEdge.setFaceToAll(null);
	}
	public   float getArea2D() {
		return FlibFace.getArea2D(this.getFaceNodes());
	}
	public  static float getArea2D(Vector<? extends HasPos2D> points) {
		float area = 0;
		int j;
		for (int i = 0; i < points.size(); i++) {
			j = i + 1;
			if (j == points.size())
				j = 0;
			HasPos2D p1 = points.get(i);
			HasPos2D p2 = points.get(j);
			area += (p1.getX() + p2.getX()) * (p2.getY() - p1.getY());
		}
		return area / 2f;
	}
	public Vector<FlibHENode> simpliFiedOutline(){
		Vector<FlibHENode>nodes=this.getFaceNodes();
		Vector<FlibHENode>simpleNodes=new Vector<FlibHENode>();
		for (int j=0;j<nodes.size();j++){
			FlibHENode n1=(FlibHENode) nodes.get(j);
			FlibHENode n2=(FlibHENode) nodes.get((j+1)%nodes.size());
			FlibHENode n3=(FlibHENode) nodes.get((j+2)%nodes.size());
			float angle=(float) Vec2D.getAngleBetweenCCW(n2, n1, n3);
			if (Math.abs(angle-Math.PI)>0.001f){
				simpleNodes.add(n2);
			}
		}
		return simpleNodes;
	}
	public void assignHedge(FlibHEdge hE,FlibFace heFace){
		hE.setFaceToAll(heFace);
		heFace.setHEdge(hE);
	}
	public boolean isPublic() {
		return type.isPublic();
	}
	public boolean isWarm() {
		return type.isWarm();
	}
	public boolean isCold() {
		return !type.isWarm();
	}
	public FlibFace clone(){
		FlibFace face=new FlibFace();
		face.type=this.type;
		face.buildingId=this.buildingId;
		face.groupId=this.groupId;
		face.levelId=this.levelId;
		return face;
	}
}
